# The class defined in this file handles the image-viewer-and-navigator
# functionality.

from geometry import Square
import gtk.gdk
import Image
from os import listdir
from imagehandling import Conversion, ImageHandling
import hildon
from time import time
from basewindow import BaseWindow

class ImageBrowser(BaseWindow):
    # Area definitions.
    PREV_F = Square((0,180), (100, 280))
    NEXT_F = Square((660,180), (760,280))
    EDIT_F = Square((330,380), (430,480))
    PREV_W = Square((0,148), (100,248))
    NEXT_W = Square((556,148), (656, 248))
    EDIT_W = Square((278,296), (378,396))
    SUPPORTED_FORMATS = ['jpg','gif','png']

    def __init__(self, program, widgets):
        """
        Constructor. Creates a new ImageBrowser instance.
        @type program: hildon.Program
        @param program: The Hildon program instance.
        @type widgets: gtk.glade.XML
        @param widgets: A reference to the widget tree of the main ui.
        """
        BaseWindow.__init__(self, program, widgets, 'MainWindow', 'Main:Vbox', 'Main:Menubar')
        
        # Get references to the needed widgets.
        self.image_widget = widgets.get_widget('Main:Image')
        self.navigate_left = widgets.get_widget('Main:LeftImage')
        self.navigate_right = widgets.get_widget('Main:RightImage')
        self.status_label = widgets.get_widget('Main:StatusLabel')
        self.about_dialog = widgets.get_widget('AboutDialog')
        self.about_dialog.set_name('AugIM')

        # Set local member vars.
        self.images = None
        self.current_index = -1
        self.original_image = None
        self.view_image = None
        self.navigate_left_pixbuf = gtk.gdk.pixbuf_new_from_file('navigate_left.png')
        self.navigate_right_pixbuf = gtk.gdk.pixbuf_new_from_file('navigate_right.png')
        self.navigate_none_pixbuf = gtk.gdk.pixbuf_new_from_file('blank.png')
        self.no_image_pixbuf = gtk.gdk.pixbuf_new_from_file('no_images.png')
        self.last_click = 0
        self.inactive_pixbuf = gtk.gdk.pixbuf_new_from_file('inactive.png')
        self.active_pixbuf = gtk.gdk.pixbuf_new_from_file('active.png')
        self.error_pixbuf = gtk.gdk.pixbuf_new_from_file('error.png')

        # Attach menu item callbacks.
        quitmenuitem = widgets.get_widget('Main:QuitMenuitem')
        quitmenuitem.connect('activate', gtk.main_quit)
        openmenuitem = widgets.get_widget('Main:OpenMenuitem')
        openmenuitem.connect('activate', self.menu_open_activated)
        settingsmenuitem = widgets.get_widget('Main:SettingsMenuitem')
        settingsmenuitem.connect('activate', self.menu_settings_activated)
        aboutmenuitem = self.widgets.get_widget('Main:AboutMenuitem')
        aboutmenuitem.connect('activate', self.menu_about_activated)

        # Register for events from the eventbox containing the image.
        eventbox = widgets.get_widget('Main:Eventbox')
        eventbox.connect('button-press-event', self.image_clicked)
        # Also register events for the navigational images.
        left_eventbox = widgets.get_widget('Main:LeftEventBox')
        left_eventbox.connect('button-press-event', self.left_clicked)
        right_eventbox = widgets.get_widget('Main:RightEventBox')
        right_eventbox.connect('button-press-event', self.right_clicked)
        # Register for events from the eventbox containing the status image.
        eventbox = widgets.get_widget('Main:StatusImageEventbox')
        eventbox.connect('button-press-event', self.status_image_clicked)

        # Set the initial mode of the window.
        self.__set_windowed_mode()
        
    def show(self):
        # Check that the window is in the correct mode.
        if self.fullscreen != self.program.fullscreen:
            self.toggle_fullscreen()
        self.window.show()
    
    def hide(self):
        # Store the fullscreen status in the program.
        self.program.fullscreen = self.fullscreen
        self.window.hide()
            
    def on_left_key_clicked(self):
        self.previous_image()

    def on_right_key_clicked(self):
        self.next_image()
    
    def fullscreen_key_clicked(self):
        self.toggle_fullscreen()
        
    def create_scaled_version(self, path):
        # This method created a scaled down version of an image.
        # The scaled down version is stored alongside the original
        # but as a hidden file with ".augim_" prefixed.
        pass

    def load_images_from_path(self, path):
        """
        Scans the given path for jpg images and adds them to the image browser.
        @type path: str
        @param path: The path to scan for images.
        @raise OSError: If the path is invalid.  
        @raise IOError: If the initial loading of the first image fails.
        """
        dir_contents = listdir(path)
        images = []
        for image_file in dir_contents:
            if image_file[-3:].lower() in ImageBrowser.SUPPORTED_FORMATS:
                images.append(path + '/' + image_file)
        self.set_images(images)
        
        # Create a popup message.
        message = "No images found in path."
        if len(self.images) != 0:
            message = "Loaded %i images from %s."%(len(self.images), path)
        self.program.popup_message(message)

    def set_images(self, images):
        """
        Sets the images that the browser should be able to show and 
        navigate through.
        @type images: list of str
        @param images: The paths of the images to browse.
        @raise IOError: If the initial loading of the first image fails.
        """
        self.original_image = None
        self.image_widget.clear()
        self.images = images
        # Display the first image in the list if there are any.
        if len(self.images) > 0:
            self.current_index = 0
            self.display_image(0)
        else:
            # There are no images.
            self.navigate_left.clear()
            self.navigate_right.clear()
            self.image_widget.set_from_pixbuf(self.no_image_pixbuf)
                
    def display_image(self, index):
        """
        Loads and displays the image at the given index in the browsers 
        image list.
        @type index: int
        @param index: The index to load and display.
        @raise IOError: If the file at the given index can not be read.
        @raise IndexError: If the index is invalid.
        """
        # Load the image into an Image.
        self.original_image = Image.open(self.images[index])
        # Scale it for view on the N800.
        self.view_image = ImageHandling.simple_scale(self.original_image, self.width, self.height)
        # Display the image in the GtkImage widget.
        self.image_widget.set_from_pixbuf(Conversion.image2pixbuf(self.view_image))
        # Update the navigation arrows.
        # Set the 'previous' navigation area.
        if index != 0:
            self.navigate_left.set_from_pixbuf(self.navigate_left_pixbuf)
        else:
            self.navigate_left.set_from_pixbuf(self.navigate_none_pixbuf)
        # ... and the 'next' navigation area.
        if index + 1 != len(self.images):
            self.navigate_right.set_from_pixbuf(self.navigate_right_pixbuf)
        else:
            self.navigate_right.set_from_pixbuf(self.navigate_none_pixbuf)
        # Update the status label.
        self.status_label.set_text(self.current_image() + ' - (%i of %i)'%(index+1, len(self.images)))
        
    def next_image(self):
        """
        Browses to the next image in the list.
        """
        # Check that there _is_ a next image.
        if self.current_index + 1 < len(self.images):
            self.current_index += 1
            self.display_image(self.current_index)

    def previous_image(self):
        """
        Browses to the previous image in the list.
        """
        # Check that there _is_ a previous image.
        if self.current_index > 0:
            self.current_index -= 1
            self.display_image(self.current_index)
            
    def current_image(self):
        path = self.images[self.current_index]
        return path[path.rfind('/')+1:]
    
    def __set_windowed_mode(self):
        self.fullscreen = False
        self.window.unfullscreen()
        self.height = 371
        self.width = 656
        self.next = ImageBrowser.NEXT_W
        self.prev = ImageBrowser.PREV_W
        self.edit = ImageBrowser.EDIT_W

    def __set_fullscreen_mode(self):
        self.fullscreen = True
        self.window.fullscreen()
        self.height = 455
        self.width = 760
        self.next = ImageBrowser.NEXT_F
        self.prev = ImageBrowser.PREV_F
        self.edit = ImageBrowser.EDIT_F
    
    def __open_path(self):
        filechooser = hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
        status = filechooser.run()
        filechooser.hide()
        if status == gtk.RESPONSE_OK:
            self.load_images_from_path(filechooser.get_filename())
    
    def menu_open_activated(self, widget):
        self.__open_path()
    
    def menu_settings_activated(self, widget):
        self.program.set_mode('config')
    
    def menu_about_activated(self, widget):
        self.about_dialog.show()
        self.about_dialog.run()
        self.about_dialog.hide()
    
    def toggle_fullscreen(self):
        """
        Switches between fullscreen and windowed mode.
        """
        # Toggle between fullscreen and windowed mode.
        if self.fullscreen:
            self.__set_windowed_mode()
        else:
            self.__set_fullscreen_mode()
        
        # Resize the image currently displayed.
        if self.images != None:
            self.view_image = ImageHandling.simple_scale(self.original_image, self.width, self.height)
            self.image_widget.set_from_pixbuf(Conversion.image2pixbuf(self.view_image))
    
    def image_clicked(self, widget, event):
        """
        Callback function that is called upon mouse clicks in the event box.
        @type widget: gtk.Widget
        @param widget: The widget that received the callback (i.e., the eventbox?).
        @type event: gtk.gdk.Event
        @param event: The generated event object. 
        """
        # Check where the click was performed.
        if self.prev.contains(event.get_coords()):
            self.previous_image()
        elif self.next.contains(event.get_coords()):
            self.next_image()
        elif self.edit.contains(event.get_coords()):
            if self.original_image:
                self.program.set_mode('edit')
            else:
                # There is no image to edit. Go to the filechooser dialog.
                self.__open_path()
        else:
            # Check for double clicks.
            now = time()
            if now - self.last_click < 1:
                if self.original_image:
                    self.program.set_mode('edit')
                else:
                    self.__open_path()
            self.last_click = now
            
    def left_clicked(self, widget, event):
        self.previous_image()
        
    def right_clicked(self, widget, event):
        self.next_image()

    def status_image_clicked(self, widget, event):
        self.program.set_mode('task')
        
    def set_status_image(self, status):
        image = self.widgets.get_widget('Main:StatusImage')
        if status == 'active':
            image.set_from_pixbuf(self.active_pixbuf)
        elif status == 'inactive':
            image.set_from_pixbuf(self.inactive_pixbuf)
        else:
            image.set_from_pixbuf(self.error_pixbuf)